package com.zhouzhou.fastjava.query;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Sets;
import org.apache.commons.io.IOUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Set;

public class JSONConfigBuilder {

    private QueryConfiguration configuration;

    private final ResourcePatternResolver resourcePatternResolver;

    private boolean parsed;

    private JSONObject source;

    private String moduleRoot;

    private Set<String> loadedModules = Sets.newHashSet();

    private JSONConfigBuilder(ResourcePatternResolver resourcePatternResolver, String source) {
        super();
        this.resourcePatternResolver = resourcePatternResolver;
        this.source = JSON.parseObject(source);
        this.configuration = new QueryConfiguration(this);
    }

    public JSONConfigBuilder(ResourcePatternResolver resourcePatternResolver, InputStream inputStream) throws Exception {
        this(resourcePatternResolver, toString(inputStream));
    }

    private static String toString(InputStream inputStream) throws Exception {
        StringBuilder str = new StringBuilder();
        byte[] bytes = new byte[128];
        int buf;
        while ((buf = inputStream.read(bytes)) > 0) {
            str.append(new String(bytes, 0, buf, Charset.forName("utf-8")));
        }
        return str.toString();
    }

    public void reloadModule(String module) throws IOException {

        // query settings
        {
            String content = getSettingContentInModule(module, "query-settings.json");
            if (content != null) {
                QuerySetting querySetting = JSON.parseObject(content, QuerySetting.class);
                configuration.getQuerySettings().put(module, querySetting);
            }
        }
        // label settings
        {
            String content = getSettingContentInModule(module, "label-settings.json");
            if (content != null) {
                LabelSetting labelSetting = JSON.parseObject(content, LabelSetting.class);
                configuration.getLabelSettings().put(module, labelSetting);
            }
        }
        // sql
        {
            String content = getSettingContentInModule(module, "sql.ftl");
            configuration.getSqls().put(module, content);
        }
        loadedModules.add(module);
    }

    private String getSettingContentInModule(String module, String filename) throws IOException {
        Resource resource = resourcePatternResolver.getResource("classpath:" + moduleRoot + "/" + module + "/" + filename);
        if (resource.exists()) {
            String content = IOUtils.toString(resource.getInputStream(), "utf-8");
            return content;
        }
        return null;
    }

    public QueryConfiguration parse() throws Exception {
        if (parsed) {
            throw new RuntimeException("Each JSONConfigBuilder can only be used once.");
        }
        parsed = true;
        parseConfiguration();
        return configuration;
    }

    private void parseConfiguration() throws Exception {
        this.moduleRoot = source.getString("moduleRoot");
        parseModuleSettings();
    }

    private void parseModuleSettings() throws Exception {
        Resource[] allResource = resourcePatternResolver.getResources("classpath*:" + moduleRoot + "/**/*");
        for (Resource resource : allResource) {
            parseModuleResource(resource, moduleRoot);
        }
    }

    private void parseModuleResource(Resource resource, String moduleRoot) throws IOException {
        String filename = resource.getFilename();
        if (!filename.contains(".")) {
            return;
        }
        String uri = resource.getURI().toString();
        String module = uri.substring(uri.indexOf(moduleRoot) + moduleRoot.length() + 1, uri.lastIndexOf("/"));
        if (!loadedModules.contains(module)) {
            reloadModule(module);
        }
    }

}
